#ifndef TCP_SERVER_HTTPREQUEST_H
#define TCP_SERVER_HTTPREQUEST_H

#include "../../base/copyable.h"
#include "../../base/Timestamp.h"
#include "../../base/Timestamp.h"

#include <map>
#include <cassert>
#include <cstdio>
#include <algorithm>
#include <boost/lexical_cast.hpp>

namespace detail {
    static bool icasecompare(const string &a, const string &b) {
        if (a.size() == b.size()) {
            return std::equal(a.begin(), a.end(), b.begin(),
                              [](char a, char b) {
                                  return tolower(a) == tolower(b);
                              });
        } else {
            return false;
        }
    }

    /**
    * @brief 获取Map中的key值,并转成对应类型,返回是否成功
    * @param[in] m Map数据结构
    * @param[in] key 关键字
    * @param[out] val 保存转换后的值
    * @param[in] def 默认值
    * @return
    * @retval true 转换成功, val 为对应的值
    * @retval false 不存在或者转换失败 val = def
    */
    template<class MapType, class T>
    bool checkGetAs(const MapType &m,
                    const std::string &key,
                    T &val,
                    const T &def = T()) {
        auto it = m.find(key);
        if (it == m.end()) {
            val = def;
            return false;
        }
        try {
            val = boost::lexical_cast<T>(it->second);
            return true;
        } catch (...) {
            val = def;
        }
        return false;
    }

    /**
    * @brief 获取Map中的key值,并转成对应类型
    * @param[in] m Map数据结构
    * @param[in] key 关键字
    * @param[in] def 默认值
    * @return 如果存在且转换成功返回对应的值,否则返回默认值
    */
    template<class MapType, class T>
    T getAs(const MapType & m,
            const std::string &key,
            const T &def = T()) {
        auto it = m.find(key);
        if (it == m.end()) {
            fprintf(stderr, "没找到");
            return def;
        }
        try {
            return boost::lexical_cast<T>(it->second);
        } catch (...) {
            fprintf(stderr, "转化失败");
            return def;
        }

    }
}

class HttpRequest : public muduo::copyable {
public:
    typedef std::map<string, string> MapType;

    enum Method {
        kInvalid, kGet, kPost, kHead, kPut, kDelete
        // Invalid是初始化的, 表无效, 其他都是HTTP请求方法
    };

    enum Version {
        kUnknown, kHttp10, kHttp11
        // http协议, 目前只支持http1.0和1.1
    };

    enum ContentType {
        kText,
        kHtml,
        kJS,
        kCss,
    };

    HttpRequest() : method_(kInvalid), version_(kUnknown) {}

    /**
     * 获取请求的文件后缀, 如果没有就默认以text的方式返回
     * @return
     */
    ContentType getFileSuffix() const {
        size_t i = path_.size() - 1;
        string suffix = "";
        while (i >= 0 && path_[i] != '.') {
            suffix.push_back(path_[i]);
            --i;
        }
        std::reverse(suffix.begin(), suffix.end());
        if (suffix == "css") {
            return kCss;
        } else if (suffix == "js") {
            return kJS;
        } else if (suffix == "html") {
            return kHtml;
        } else {
            return kText;
        }
    }

    void setVersion(Version v) { version_ = v; }

    Version getVersion() const { return version_; }

    bool setMethod(const char *start, const char *end) {
        assert(method_ == kInvalid);
        string m(start, end);
        // 设置忽略大小写匹配
        if (detail::icasecompare(m, "GET")) {
            method_ = kGet;
        } else if (detail::icasecompare(m, "POST")) {
            method_ = kPost;
        } else if (detail::icasecompare(m, "HEAD")) {
            method_ = kHead;
        } else if (detail::icasecompare(m, "PUT")) {
            method_ = kPut;
        } else if (detail::icasecompare(m, "DELETE")) {
            method_ = kDelete;
        } else {
            method_ = kInvalid;
        }
        return method_ != kInvalid;
    }

    Method method() const { return method_; }

    const char *methodString() const {
        const char *result = "UNKNOWN";
        switch (method_) {
            case kGet:
                result = "GET";
                break;
            case kPost:
                result = "POST";
                break;
            case kHead:
                result = "HEAD";
                break;
            case kPut:
                result = "PUT";
                break;
            case kDelete:
                result = "DELETE";
                break;
            default:
                break;
        }
        return result;
    }

    /**
     * 设置请求路径
     * @param start
     * @param end
     */
    void setPath(const char *start, const char *end) { path_.assign(start, end); }

    const string &getPath() const { return path_; }

    /**
     * 跟在path的?后面的参数
     * @param start
     * @param end
     */
    void setQuery(const char *start, const char *end) { query_.assign(start, end); }

    const string &getQuery() const { return query_; }

    void setReceiveTime(Timestamp t) { receiveTime_ = t; }

    Timestamp receiveTime() const { return receiveTime_; }

    /**
     * 添加http请求头部的key - val键值对
     * @param start 起始读取位置
     * @param colon 分隔位置
     * @param end 结束位置
     */
    void addHeader(const char *start, const char *colon, const char *end) {
        string field(start, colon);
        ++colon;
        while (colon < end && isspace(*colon)) { ++colon; }
        // 指针后移用于解析key对应的val
        string value(colon, end);
        int rsize = value.size();
        while (!value.empty() && isspace(value[value.size() - 1])) {
            value.resize(value.size() - 1);
        }
        /*  while(rsize >= 0 && isspace(value[rsize - 1])) rsize--;
          value.resize(rsize);*/
        // FIXME: 看看会不会出问题
        headers_[field] = value;
    }

    /**
     * 获取指定键对应的值
     * @param field http头部字段名
     * @return 对应的值
     */
    string getHeader(const string &field) const {
        string result;
        MapType::const_iterator it = headers_.find(field);
        if (it != headers_.end()) {
            result = it->second;
        }
        return result;
    }

    /**
     * 获取httprequest的所有请求头
     * @return 所有请求头的map集合封装
     */
    const MapType &headers() const { return headers_; }

/*    void setBody(const string & body){ body_ = body; }
    const string & getBody() const{ return body_; }*/
    void setBody(const MapType &body) { body_ = body; }

    /**
     * 获取请求体map
     * @return 请求体map
     */
    const MapType &body() const { return body_; }

    /**
     * 获取单个请求的value
     * @param field 请求key
     * @return 返回请求体value
     */
    string getBody(const string &field) const {
        string result;
        MapType::const_iterator it = body_.find(field);
        if (it != body_.end()) {
            result = it->second;
        }
        return result;
    }

    /**
     * 添加http请求头部的key - val键值对
     * @param start 起始读取位置
     * @param equal 分隔位置
     * @param end 结束位置
     */
    void addBody(const char *start, const char *equal, const char *end) {
        string field(start, equal);
        ++equal;
        while (equal < end && isspace(*equal)) { ++equal; }
        // 指针后移用于解析key对应的val
        string value(equal, end);
        int rsize = value.size();
        while (!value.empty() && isspace(value[value.size() - 1])) {
            value.resize(value.size() - 1);
        }
        // FIXME: 看看会不会出问题
        body_[field] = value;
    }

    void swap(HttpRequest &that) {
        std::swap(method_, that.method_);
        std::swap(version_, that.version_);
        path_.swap(that.path_);
        query_.swap(that.query_);
        receiveTime_.swap(that.receiveTime_);
        headers_.swap(that.headers_);
        std::swap(body_, that.body_);
    }

    /* 类型转化 */
    template<class T>
    bool getHeaderCheckAs(const std::string &key,
                    T &val,
                    const T &def = T()) const{
        return detail::checkGetAs<MapType, T>(headers_,
                             key,
                             val);
    }

    template<class T>
    T getHeaderAs(const std::string &key, const T &def = T()) const{
        return detail::getAs<MapType, T>(headers_, key);
    }

    template<class T>
    bool getBodyCheckAs(const std::string &key,
                          T &val,
                          const T &def = T()) const{
        return detail::checkGetAs<MapType, T>(body_,
                             key,
                             val);
    }

    template<class T>
    T getBodyAs(const std::string &key, const T &def = T()) const {
        return detail::getAs<MapType, T>(body_, key);
    }

private:
    Method method_;
    Version version_;
    string path_;
    string query_;
    Timestamp receiveTime_;
    // 接收时间
    MapType headers_;
    // 请求头部
    //string body_;
    MapType body_;
    // 请求体元素
};

#endif //TCP_SERVER_HTTPREQUEST_H
